/*
 * Copyright (c) 2008-2016, RF-Embedded GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "TestProgram.h"

#include <helper/StringHelper.h>
#include <helper/Sleeper.h>

#include "impl/QtDevice.h"
#include "impl/ConsoleTrace.h"
#include "impl/Timer.h"


#include <iostream>
#include <set>

#include <QTcpSocket>
#include <QHostAddress>
#include <QSerialPort>
#include <QTime>

using namespace CPPrfeReaderInterface;


// ************************************************* Change settings here
#define TEST_PROGRAM_USE_TCP        0
#define TEST_PROGRAM_TCP_IP         "127.0.0.1"
#define TEST_PROGRAM_TCP_PORT       52475
#define TEST_PROGRAM_SERIAL_PORT    "COM19"

TestProgram::TestProgram()
{
    m_ph = 0;
}

TestProgram::~TestProgram()
{
    if(m_ph)
        delete m_ph;

    if(Global::m_tracer)
        delete Global::m_tracer;
}

bool TestProgram::init()
{
    // create trace and turn off
    Global::m_tracer = new ConsoleTrace();
    Global::m_tracer->setTraceLevel(0);

    /*********************************************************************************/// QT BEGIN
#if TEST_PROGRAM_USE_TCP
    QString ip = TEST_PROGRAM_TCP_IP;
    ushort port = TEST_PROGRAM_TCP_PORT;
    QTcpSocket* dev = new QTcpSocket();
    dev->connectToHost(QHostAddress(ip), port);
    if(!dev->waitForConnected())
    {
        print("Could not connect to " + ip.toStdString() + ":" + StringHelper::toIntString(port));
        delete dev;
        return false;
    }
#else
    // Open serial port
    QSerialPort* dev = new QSerialPort();
    dev->setPortName(TEST_PROGRAM_SERIAL_PORT);
    dev->setFlowControl(QSerialPort::NoFlowControl);
    dev->setParity(QSerialPort::NoParity);
    dev->setDataBits(QSerialPort::Data8);
    dev->setStopBits(QSerialPort::OneStop);
    dev->setBaudRate(QSerialPort::Baud115200);
    if(!dev->open(QIODevice::ReadWrite))
    {
        print("Could not open the serial port " + std::string(TEST_PROGRAM_SERIAL_PORT));
        delete dev;
        return false;
    }
#endif

    QtDevice* device = new QtDevice(dev);
    if(!device->open())
    {
        print("Could not open socket...");
        delete device;
        return false;
    }

    m_ph = new CPPrfePURprotocolHandler(device);
    m_ph->setEventListener(this);
    /*********************************************************************************/// QT END

    return true;
}


void TestProgram::print(const char* str)
{
   std::cout << str << std::endl;
}

void TestProgram::print(const std::string& str)
{
    std::cout << str << std::endl;
}




void TestProgram::test_Inventory()
{
    print("Testing Cyclic Inventory:");

    // turn on cyclic inventory
    print("\t -> 1, Starting Cyclic Inventory");
    m_cyclicInvCount = 0;
    m_ph->setCyclicInventory(true);

    print("");
    uint waitTime = 10000;
    Sleeper::msleep(waitTime);

    // turn off cyclic inventory and calculate read rate
    m_ph->setCyclicInventory(false);
    double readRate = (double)m_cyclicInvCount / ((double)waitTime/1000.0);
    print("\t -> 2, Stopped Cyclic Inventry with a ReadRate of " + StringHelper::toIntString(readRate) + " reads/sec");
}




void TestProgram::HeartBeatHandler(const std::vector<byte> &data)
{
    print("\t[E] Heartbeat " + StringHelper::toString(data));
}

void TestProgram::CyclicInventoryHandler(const CPPrfeTagEvent &tagInfo)
{
    if(!m_lastTagEventValid)
    {
        m_lastTagEventValid = true;
        m_lastTagEvent = tagInfo;
    }

    std::string info;
    info = "\t[E] " + StringHelper::toIntString(++m_cyclicInvCount, 8) + " " + StringHelper::toString(tagInfo.tagId) + " ";

    if (tagInfo.hasMemory)
    {
        info += " MEM@" + StringHelper::toIntString(tagInfo.memBank) + "." + StringHelper::toIntString(tagInfo.memAddr) + ":" + StringHelper::toString(tagInfo.memData) + " ";
    }

    if (tagInfo.hasApplicationInfo)
    {
        info += " APP:" + StringHelper::toString(tagInfo.applicationInfo) + " ";
    }

    print(info);

    if (tagInfo.hasRSSI)
    {
        std::string q_str;
        q_str = "\t\t\tQ: ";
        for (int i = 0; i < tagInfo.rssi[0]; i++)
            q_str.append("#");
        print(q_str);

        std::string i_str;
        i_str = "\t\t\tI: ";
        for (int i = 0; i < tagInfo.rssi[1]; i++)
            i_str.append("#");
        print(i_str);
    }
}

void TestProgram::StateChangedHandler(eRFE_CURRENT_READER_STATE newState)
{
    print("\t[E] State changed to: " + StringHelper::toString(newState));
}

void TestProgram::StatusRegisterChangedHandler(ulonglong statusRegister)
{
    print("\t[E] Status register changed " + StringHelper::toHexString(statusRegister));
}

void TestProgram::GpioValuesChangedHandler(ulong gpioValues)
{
    print("\t[E] GPIO values changed " + StringHelper::toHexString(gpioValues));
}

void TestProgram::NotificationHandler(const std::vector<byte> &payload)
{
    print("\t[E] Notification " + StringHelper::toString(payload));
}

void TestProgram::ApplicationEventHandler(const std::vector<byte> &payload)
{
    print("\t[E] ApplicationEvent " + StringHelper::toString(payload));
}
